//
//  RisacTextView.swift
//  Risac
//
//  Created by remy on 2018/7/26.
//

import SnapKit

public protocol RisacTextViewDelegate: AnyObject {
    func textViewEditBegin(view: RisacTextView, textView: UITextView)
    func textViewEditEnd(view: RisacTextView, textView: UITextView)
    func textViewEditChanged(view: RisacTextView, textView: UITextView)
    func textViewHeightChanged(view: RisacTextView, textView: UITextView, height: CGFloat)
    func textViewCountChanged(view: RisacTextView, textView: UITextView, count: (Int, Int))
}
extension RisacTextViewDelegate {
    func textViewEditBegin(view: RisacTextView, textView: UITextView) {}
    func textViewEditEnd(view: RisacTextView, textView: UITextView) {}
    func textViewEditChanged(view: RisacTextView, textView: UITextView) {}
    func textViewHeightChanged(view: RisacTextView, textView: UITextView, height: CGFloat) {}
    func textViewCountChanged(view: RisacTextView, textView: UITextView, count: (Int, Int)) {}
}

open class RisacTextView: UIView {
    
    public let textView = UITextView()
    public weak var delegate: RisacTextViewDelegate?
    public var contentInset: UIEdgeInsets = .zero {
        didSet { triggerLayout(0b01) }
    }
    public var text: String {
        get { return textView.text ?? "" }
        set {
            textView.text = newValue
            checkTextCount(newValue)
            placeholderLabel.isHidden = !newValue.isEmpty
        }
    }
    public var keyboardType: UIKeyboardType {
        get { return textView.keyboardType }
        set { textView.keyboardType = newValue }
    }
    public private(set) lazy var placeholderLabel: UILabel = {
        let view = UILabel(frame: .zero, font: textView.font)
        view.numberOfLines = 0
        return view
    }()
    public var maxTextLength: Int = 0 {
        didSet { triggerLayout(0b10) }
    }
    private var previousViewHeight: CGFloat = 0.0
    public var viewHeight: CGFloat {
        return ceil(textView.sizeThatFits(CGSize(width: textView.width, height: CGFloat.greatestFiniteMagnitude)).height + contentInset.top + contentInset.bottom)
    }
    /// 更新规则
    private var layoutFlag: (Bool, Int) = (false, 0)
    
    public init(frame: CGRect = .zero, closure: ((RisacTextView, UITextView) -> Void)? = nil) {
        super.init(frame: frame)
        textView.textContainerInset = UIEdgeInsets.zero
        textView.textContainer.lineFragmentPadding = 0.0
        textView.delegate = self
        self.addSubview(textView)
        textView.snp.makeConstraints { (make) in
            make.edges.equalTo(contentInset)
        }
        self.addSubview(placeholderLabel)
        placeholderLabel.snp.makeConstraints { (make) in
            make.top.equalTo(contentInset.top)
            make.left.equalTo(contentInset.left)
            make.right.equalTo(-contentInset.right)
            make.bottom.lessThanOrEqualTo(-contentInset.bottom)
        }
        closure?(self, textView)
    }
    
    required public init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    public func triggerLayout(_ flag: Int) {
        self.layoutFlag.1 |= flag
        if !layoutFlag.0 {
            layoutFlag.0 = true
            QueueTask.main { [weak self] in
                self?.refreshLayout()
            }
        }
    }
    
    private func refreshLayout() {
        if layoutFlag.1 & 0b01 > 0 {
            textView.snp.updateConstraints { (make) in
                make.edges.equalTo(contentInset)
            }
            let textContainerInset = textView.textContainerInset
            placeholderLabel.snp.updateConstraints { (make) in
                make.top.equalTo(contentInset.top + textContainerInset.top)
                make.left.equalTo(contentInset.left + textContainerInset.left)
                make.right.equalTo(-contentInset.right - textContainerInset.right)
                make.bottom.lessThanOrEqualTo(-contentInset.bottom - textContainerInset.bottom)
            }
        }
        if layoutFlag.1 & 0b10 > 0 {
            textViewDidChange(textView)
        }
        layoutFlag.0 = false
    }
    
    public func focus() {
        textView.becomeFirstResponder()
    }
    
    public func blur() {
        textView.resignFirstResponder()
    }
    
    private func checkTextCount(_ text: String) {
        guard maxTextLength > 0 else { return }
        if text.count > maxTextLength {
            self.text = text.substring(to: maxTextLength)
            return
        }
        delegate?.textViewCountChanged(view: self, textView: textView, count: (text.count, maxTextLength - text.count))
    }
}

extension RisacTextView: UITextViewDelegate {
    
    public func textViewDidBeginEditing(_ textView: UITextView) {
        delegate?.textViewEditBegin(view: self, textView: textView)
    }
    
    public func textViewDidEndEditing(_ textView: UITextView) {
        self.text = textView.text.trim
        delegate?.textViewEditEnd(view: self, textView: textView)
    }
    
    public func textViewDidChange(_ textView: UITextView) {
        placeholderLabel.isHidden = !textView.text.isEmpty
        // 统计剩余字数
        if maxTextLength > 0 {
            let text = textView.text ?? ""
            if let selectedRange = textView.markedTextRange {
                if textView.position(from: selectedRange.start, offset: 0) != nil {
                    // 标记输入,则暂不对文字进行统计和限制
                    return
                }
            }
            // 实际输入,则对已输入的文字进行字数统计和限制
            checkTextCount(text)
        }
        // 计算视图高度
        let height = self.viewHeight
        if previousViewHeight != height {
            previousViewHeight = height
            delegate?.textViewHeightChanged(view: self, textView: textView, height: height)
        }
        // 文字内容更新
        guard textView.markedTextRange != nil else {
            delegate?.textViewEditChanged(view: self, textView: textView)
            return
        }
    }
}
